package cn.mutsuki.micrometer.core;

import io.micrometer.core.instrument.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.time.Duration;
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.ToDoubleFunction;

@Slf4j
public class MetricTemplate {
    private MeterRegistry meterRegistry;
    private ConcurrentHashMap<Meter.Id, Timer.Sample> sampleMap = new ConcurrentHashMap<>();

    @Autowired
    public MetricTemplate(MeterRegistry meterRegistry){
        this.meterRegistry = meterRegistry;
    }

    public MeterRegistry getMeterRegistry(){
        return meterRegistry;
    }

    public double counterAdd(String counterName , String... tags){
        return counterAdd(counterName , 1 , tags);
    }

    public double counterAdd(String counterName , double increment ,String... tags){
        Counter counter = meterRegistry.counter(counterName, tags);
        counter.increment(increment);
        return counter.count();
    }

    public void gaugeSet(String gaugeName , ToDoubleFunction<Object> f , Object target , String... tags){
        ArrayList<Tag> tagList = new ArrayList<>();
        if(tags.length %2 == 1) throw new IllegalArgumentException("Tags length is not correctly, length: " + tags.length);
        for(int i = 0 ; i < tags.length ; i+=2){
            tagList.add(Tag.of(tags[i] , tags[i+1]));
        }
        meterRegistry.gauge(gaugeName , tagList , target , f);
    }

    public void timerSetByRecordCallable(String timerName , Callable<Object> callable , String... tags){
        Timer timer = meterRegistry.timer(timerName , tags);
        try{
            timer.recordCallable(callable);
        }catch(Exception e) {
            log.error(e.getMessage());
            timer.close();
        }
    }

    public void timerSetByRecordRunnable(String timerName , Runnable runnable , String... tags){
        Timer timer = meterRegistry.timer(timerName , tags);
        timer.record(runnable);
    }

    public void timerSetByRecordDuration(String timerName , Duration duration , String... tags){
        Timer timer = meterRegistry.timer(timerName , tags);
        timer.record(duration);
    }

    public Meter.Id startTimer(String timerName , String... tags){
        Tags meterTags = Tags.of(tags);
        Meter.Id meterIdentity = new Meter.Id(timerName , meterTags , null , null , Meter.Type.TIMER);
        Timer.Sample sample = Timer.start();
        sampleMap.put(meterIdentity , sample);
        return meterIdentity;
    }

    public void stopTimer(Meter.Id meterIdentity){
        Timer.Sample sample = sampleMap.remove(meterIdentity);
        sample.stop(meterRegistry.timer(meterIdentity.getName() , meterIdentity.getTags()));
    }

    public void longTaskTimerSetByRecordRunnable(String timerName , Runnable runnable , String... tags){
        LongTaskTimer timer = meterRegistry.more().longTaskTimer(timerName , tags);
        timer.record(runnable);
    }

    public void longTaskTimerSetByRecordCallable(String timerName , Callable callable , String... tags){
        LongTaskTimer timer = meterRegistry.more().longTaskTimer(timerName , tags);
        try{
            timer.recordCallable(callable);
        }catch(Exception e) {
            log.error(e.getMessage());
            timer.close();
        }
    }


}
